use std::{fs, sync::Arc, vec};

use arc_swap::ArcSwapOption;
use once_cell::sync::Lazy;
use shq_common::prelude::{get_work_path, AuthType, MenuItem};

use crate::get_user_perm;

pub struct Menus(Vec<MenuItem>);
impl Menus {
    pub fn from(perms: Vec<MenuItem>) -> Self {
        Menus(perms)
    }
    pub fn get_menus_by_pid(&self,pid:Option<u32>) -> Option<&[MenuItem]> {
        if let Some(pid) = pid {
            for perm in &self.0 {
                if perm.id == pid {
                    return Some(&perm.children);
                }
            }
            None
        }else{
            return Some(&self.0);
        }
    }
    pub async fn get_menus_by_pid_user_id(&self, pid: Option<u32>, user_id: u32) -> Vec<MenuItem> {
        let mut res: Vec<MenuItem> = Vec::new();
        if let Some(pid) = pid {
            for p in &self.0 {
                if p.id == pid {
                    if let Some(np) = filter_by_user_id(p, user_id).await {
                        res.push(np);
                    }
                }
            }
        } else {
            for p in &self.0 {
                if let Some(np) = filter_by_user_id(p, user_id).await {
                    res.push(np);
                }
            }
        };
        return res;
    }
}
static CONF: Lazy<ArcSwapOption<&'static Menus>> = Lazy::new(|| ArcSwapOption::const_empty());
pub fn init_menus_conf() -> anyhow::Result<()> {
    let file_path = format!("{}/config_files/menus.yaml", get_work_path());
    let conf_str = fs::read_to_string(file_path)?;
    let perms: Vec<MenuItem> = serde_yaml::from_str(&conf_str).or_else(|err| {
        Err(anyhow::anyhow!(
            "解析配置文件permission.yaml出错，错误信息{}",
            err
        ))
    })?;
    let perms = Menus::from(perms);
    let perms_ref = Box::leak(Box::new(perms));
    CONF.store(Some(Arc::new(perms_ref)));
    Ok(())
}
pub fn get_menus_conf() -> &'static Menus {
    CONF.load().clone().unwrap().as_ref()
}

async fn filter_by_user_id(mi: &MenuItem, user_id: u32) -> Option<MenuItem> {
    if mi.children.is_empty() {
        let perm = get_user_perm(user_id, mi.id).await.unwrap_or(None);
        let has_auth=perm.as_ref().map(|p| {
            if p.auth_types.is_empty() {
                return false;
            }
            if p.auth_types.contains(&AuthType::Select) {
                return true;
            }
            false
        })?;
        if has_auth {
            let new_mi=MenuItem{
                id: mi.id,
                name: mi.name.clone(),
                component: mi.component.clone(),
                permission: perm,
                children: vec![],
            };
            return Some(new_mi);
        }
    }
    let mut children: Vec<MenuItem> = Vec::new();
    for child in &mi.children {
        Box::pin(filter_by_user_id(child, user_id))
            .await
            .map(|c| children.push(c));
    }
    if children.is_empty() {
        return None;
    }
    let mut mi = mi.clone();
    mi.children = children;
    Some(mi)
}
